#ifndef TB_ARCHIVE_MESSAGE_PACK_READER_H__
#define TB_ARCHIVE_MESSAGE_PACK_READER_H__

#include "../../base/tostring.hpp"

TB_NAMESPACE_BEGIN

namespace detail {
	struct MsgPackReadHelper {
		void Read(const char* data, int64& val) {
			uint8 typeCode = *reinterpret_cast<const uint8*>(data);
			if (typeCode == 0xd3) {
				val = *reinterpret_cast<const int64*>(data + sizeof(8));
			} else {
				val = 0;
			}
		}

		void Read(const char* data, _STD string& val) {
			uint8 typeCode = *reinterpret_cast<const uint8*>(data);
			uint32 size = 0;
			uint32 offset = sizeof(uint8);
			switch (typeCode) {
				case 0xd9: { //str 8
					size = *reinterpret_cast<const uint8*>(data);
					offset += sizeof(uint8);
					break;
				}
				case 0xda: { //str 16
					size = *reinterpret_cast<const uint16*>(data);
					offset += sizeof(uint16);
					break;
				}
				case 0xdb: { //str 32
					size = *reinterpret_cast<const uint32*>(data);
					offset += sizeof(uint32);
					break;
				}
				default: {
					return;
				}
			}
			val = _STD string(data + offset, size);
		}
	};
}

template <typename ReadBuffer>
class MsgPackReader {
 public:
  BOOST_STATIC_ASSERT(is_read_buffer<ReadBuffer>::value);

  typedef ReadBuffer read_buffer;

  MsgPackReader(read_buffer& rb) : input_(rb) {}

  bool Parse() {
    return true;
  }

  void ReadValue(Variant& obj) {
    return obj.Assign(InternalReadValue());
  }

 private:
  Variant InternalReadValue() {
    uint8 typeCode  = 0;
    input_.ReadBytes(sizeof(typeCode), reinterpret_cast<char*>(&typeCode));

    switch (typeCode) {
			case 0xc2: { //false
				return Variant::FromValue(false);
			}
			case 0xc3: { //false
				return Variant::FromValue(true);
			}
			case 0xd0: { //int8
				int8 val;
				input_.ReadBytes(sizeof(val), reinterpret_cast<char*>(&val));
				return Variant::FromValue(val);
			}
			case 0xcc: { //uint8
				uint8 val;
				input_.ReadBytes(sizeof(val), reinterpret_cast<char*>(&val));
				return Variant::FromValue(val);
			}
			case 0xd1: { //int16
				int16 val;
				input_.ReadBytes(sizeof(val), reinterpret_cast<char*>(&val));
				return Variant::FromValue(val);
			}
			case 0xcd: { //uint16
				uint16 val;
				input_.ReadBytes(sizeof(val), reinterpret_cast<char*>(&val));
				return Variant::FromValue(val);
			}
			case 0xd2: { //int32
				int32 val;
				input_.ReadBytes(sizeof(val), reinterpret_cast<char*>(&val));
				return Variant::FromValue(val);
			}
			case 0xce: { //uint32
				uint32 val;
				input_.ReadBytes(sizeof(val), reinterpret_cast<char*>(&val));
				return Variant::FromValue(val);
			}
			case 0xd3: { //int64
				int64 val;
				input_.ReadBytes(sizeof(val), reinterpret_cast<char*>(&val));
				return Variant::FromValue(val);
			}
			case 0xcf: { //uint64
				uint64 val;
				input_.ReadBytes(sizeof(val), reinterpret_cast<char*>(&val));
				return Variant::FromValue(val);
			}
			case 0xca: { //float
				float val;
				input_.ReadBytes(sizeof(val), reinterpret_cast<char*>(&val));
				return Variant::FromValue(val);
			}
			case 0xcb: { //double
				double val;
				input_.ReadBytes(sizeof(val), reinterpret_cast<char*>(&val));
				return Variant::FromValue(val);
			}
			case 0xd9: { //str 8
				uint8 size;
				input_.ReadBytes(sizeof(size), reinterpret_cast<char*>(&size));
				return Variant::FromValue(ReadString(size));
			}
			case 0xda: { //str 16
				uint16 size;
				input_.ReadBytes(sizeof(size), reinterpret_cast<char*>(&size));
				return Variant::FromValue(ReadString(size));
			}
			case 0xdb: { //str 32
				uint32 size;
				input_.ReadBytes(sizeof(size), reinterpret_cast<char*>(&size));
				return Variant::FromValue(ReadString(size));
			}
			case 0xc9: { //object
				uint32 classSize = 0;
				input_.ReadBytes(sizeof(classSize), reinterpret_cast<char*>(&classSize));
				std::string className = ReadString();
				uint32 propertySize = 0;
				input_.ReadBytes(sizeof(propertySize), reinterpret_cast<char*>(&propertySize));
				if (!className.empty()) {
					return ReadObject(className, propertySize);
				}
				break;
			}
			case 0xdc: { //fix array up to 2^16-1 elements
				uint16 size;
				input_.ReadBytes(sizeof(size), reinterpret_cast<char*>(&size));
				return ReadArray(size);
			}
			case 0xdd: { //fix array up to 2^32-1 elements
				uint32 size;
				input_.ReadBytes(sizeof(size), reinterpret_cast<char*>(&size));
				return ReadArray(size);
			}
			case 0xde: { //fix map up to 2^16-1 elements
				uint16 size;
				input_.ReadBytes(sizeof(size), reinterpret_cast<char*>(&size));
				return ReadMap(size);
			}
			case 0xdf: { //fix map up to 2^32-1 elements
				uint32 size;
				input_.ReadBytes(sizeof(size), reinterpret_cast<char*>(&size));
				return ReadMap(size);
			}
			default: {
				if ((typeCode & 0xf0) == 0x90) { //fix array up to 15 elements
					return ReadArray(typeCode & 0x0f);
				} else if ((typeCode & 0xf0) == 0x80) { //fix map up to 15 elements
					return ReadMap(typeCode & 0x0f);
				} 
				break;
      }
    }

    return Variant();
  }

  std::string ReadString() {
    uint8 typeCode  = 0;
    input_.ReadBytes(sizeof(typeCode), reinterpret_cast<char*>(&typeCode));

    switch (typeCode) {
			case 0xd9: { //str 8
				uint8 size;
				input_.ReadBytes(sizeof(size), reinterpret_cast<char*>(&size));
				return ReadString(size);
			}
			case 0xda: { //str 16
				uint16 size;
				input_.ReadBytes(sizeof(size), reinterpret_cast<char*>(&size));
				return ReadString(size);
			}
			case 0xdb: { //str 32
				uint32 size;
				input_.ReadBytes(sizeof(size), reinterpret_cast<char*>(&size));
				return ReadString(size);
			}
    }
    return "";
  }

  std::string ReadString(uint32 size) {
    std::string str;
    if (size > 0) {
      str.resize(size);
      input_.ReadBytes(size, &str.front());
    }
    return str;
  }

  Variant ReadObject(const std::string& className, uint32 size) {
    TB::String className_ = TB_STRING(className);
		if (const reflection::TypeInfo* info = reflection::GetTypeInfo(className_.c_str())) {
			Variant v = reflection::ConstructType(info, true);
			const reflection::ConstPropertiesType& properties = reflection::GetProperties(info->id);
      for (uint32 i = 0; i < size; ++i) {
        TB::String propName = TB_STRING(ReadString());
        Variant val = InternalReadValue();

				reflection::ConstPropertiesType::const_iterator findIter
          = std::find_if(properties.begin(), properties.end(), 
          [&propName](const reflection::Property* prop) -> bool {
						return prop->name == propName;
          }
        );

        if (findIter != properties.end() && !val.IsNull()) {
          (*findIter)->Set(v.DataPtr(), val);
        }
      }

      return v;
    }
    return Variant();
  }

  Variant ReadArray(uint32 size) {
    Variant v(0, Variant::kComplexTypeFlag, Variant::kArrayDataFlag);
    for (uint32 i = 0; i < size; ++i) {
      v.PushBack(InternalReadValue());
    }
    return v;
  }

  Variant ReadMap(uint32 size) {
    Variant v(0, Variant::kComplexTypeFlag, Variant::kMapDataFlag);
    for (uint32 i = 0; i < size; ++i) {
			//independ to the call convention
			Variant key  = InternalReadValue();
			Variant value = InternalReadValue();
			v.Insert(key, value);
    }
    return v;
  }

 private:
  read_buffer& input_;
};

TB_NAMESPACE_END

#endif // TB_ARCHIVE_MESSAGE_PACK_READER_H__
